/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package core;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import org.apache.log4j.Logger;
import ui.DbVersioning;

/**
 *
 * @author Sakis Vtdk
 */
public class ChangesLogger
{
	private ChangesLogger()
	{
		//TODO: possible initialisation of the object?
	}
	
	public static ChangesLogger getChangesLogger()
	{
		if(ChangesLogger._instance == null)
		{
			ChangesLogger._instance = new ChangesLogger();
		}

		return ChangesLogger._instance;
	}

	/**
	 * Deletes all changes within the logger
	 */
	public void deleteAllChanges()
	{
		this._changes.clear();
	}
	
	public ArrayList<Change> getChanges()
	{
		return this._changes;
	}


	/**
	 * Gets a specific subset of the changes from <code>versionStart</code> (inclusive)
	 * up to <code>versionEnd</code> (inclusive)
	 * 
	 * @param versionStart The minimum version of the changes
	 * @param versionEnd The maximum version of the changes
	 * 
	 * @return An <code>ArrayList</code> of the {@link Change}s between these versions
	 */
	public ArrayList<Change> getChanges(BigDecimal versionStart, BigDecimal versionEnd)
	{
		ArrayList<Change> changesSubset = new ArrayList();

		for(Change change : _changes)
		{
			if(change.getTargetVersion().compareTo(versionStart) >= 0 && change.getTargetVersion().compareTo(versionEnd) <= 0)
			{
				changesSubset.add(change);
			}
		}

		return changesSubset;
	}

	/**
	 * Gets all the changes that happened from the version
	 * <code>minimumVersion</code> (inclusive) up until now
	 * 
	 * @param minimumVersion The minimum version to fetch the changes
	 * 
	 * @return An <code>ArrayList</code> of the {@link Change}s from that version and after
	 */
	public ArrayList<Change> getChangesFromVersion(BigDecimal minimumVersion)
	{
		ArrayList<Change> changesSubset = new ArrayList();

		for(Change change : _changes)
		{
			if(change.getTargetVersion().compareTo(minimumVersion) >= 0)
			{
				changesSubset.add(change);
			}
		}
		
		return changesSubset;
	}

	/**
	 * Gets all the changes that happened for a specific version
	 * 
	 * @param version The version of the changes to get
	 * @return An <code>ArrayList</code> of the {@link Change}s happened in
	 * that version
	 */
	public ArrayList<Change> getChangesForVersion(BigDecimal version)
	{
		ArrayList<Change> changesSubset = new ArrayList();

		for(Change change : _changes)
		{
			if(change.getTargetVersion().compareTo(version) == 0)
			{
				changesSubset.add(change);
			}
		}
		
		return changesSubset;
	}

	/**
	 * Gets all the changes that happened from the beginning until the version
	 * <code>maximumVersion</code> (inclusive)
	 * 
	 * @param maximumVersion The maximum target version of the changes
	 * 
	 * @return An <code>ArrayList</code> of the {@link Change}s from the beginning 
	 * and up until the <code>maximumVersion</code>
	 */
	public ArrayList<Change> getChangesUpToVersion(BigDecimal maximumVersion)
	{
		ArrayList<Change> changesSubset = new ArrayList();

		for(Change change : _changes)
		{
			if(change.getTargetVersion().compareTo(maximumVersion) <= 0)
			{
				changesSubset.add(change);
			}
		}

		return changesSubset;
	}

	/**
	 * Adds a new {@link Change} in the {@link ChangesLogger}
	 * 
	 * @param change The {@link Change} to be added
	 */
	public void addChange(Change change)
	{
		boolean changeExisted = false;

		//if the change is an update, we may have to update an existing change
		if(change.getType().equals(Change.Type.UPDATE.toString()))
		{
			ArrayList<Change> changes = getChangesForVersion(change.getTargetVersion());

			for(Change ch : changes)
			{
				if(ch.getType().equals(change.getType()) && 
				   ch.getObjectChanged().equals(change.getObjectChanged()) &&
				   ch.getWhatChanged().equals(change.getWhatChanged()) &&
				   ch.getParentId() == change.getParentId() && 
				   ch.getId() == change.getId() && 
				   ch.getNewValue().equals(change.getOldValue()) &&
				  !ch.getOldValue().equals(change.getNewValue()))
				{
					ch.setNewValue(change.getNewValue());

					//update the temporary columns of the sqlScriptGenerator
					SqlScriptGenerator sqlScriptGenerator = SqlScriptGeneratorFactory.getSqlScriptGenerator(DbVersioning.DEFAULT_SQL_ENGINE);
					sqlScriptGenerator.updateTmpColumns(change);

					//TODO: Check the changeSummary
					changeExisted = true;
					log.debug("Change has been updated in ChangesLogger");
				}
			}
		}

		//if a similar change wasn't found, add it
		if(!changeExisted)
		{
			this._changes.add(change);	
			log.debug("The change has been added in ChangesLogger");
		}
	}

	/**
	 * Clears the list of changes
	 */
	public void clearChanges()
	{
		log.debug("Clearing changes...");
		this._changes.clear();
	}

	/**
	 * Creates a hashmap containing the number of changes that happened in each version
	 * 
	 * @return The hashmap with the versions and the number of changes
	 */
	public LinkedHashMap getChangesPerVersions()
	{
		log.trace("Getting version changes...");
		LinkedHashMap<BigDecimal, Integer> versionsAndChanges = new LinkedHashMap();

		if(_changes.size() > 0)
		{
			Change firstChange = _changes.get(0);
			int numberOfChanges = 0;

			versionsAndChanges.put(firstChange.getTargetVersion(), numberOfChanges);

			for(Change change : _changes) //we check again for the first element but it's relatively "cheap"
			{
				if(firstChange.getTargetVersion().compareTo(change.getTargetVersion()) == 0)
				{
					numberOfChanges++;
					versionsAndChanges.put(change.getTargetVersion(), numberOfChanges);
				}
				else //if the next set of changes is found
				{
					//re-initialize the variables for the new set of changes
					firstChange = change;
					numberOfChanges = 1;
					versionsAndChanges.put(change.getTargetVersion(), numberOfChanges);
				}
			}
		}

		return versionsAndChanges;
	}

	/**
	 * Gets a list of the target versions contained in this {@link ChangesLogger}
	 * 
	 * @return An <code>ArrayList</code> containing the target versions found.
	 */
	public ArrayList<BigDecimal> getTargetVersionsList()
	{
		ArrayList<BigDecimal> targetVersionsList = new ArrayList();

		BigDecimal tmpVersion = _changes.get(0).getTargetVersion();
		targetVersionsList.add(tmpVersion);

		for(Change change : _changes)
		{
			if(change.getTargetVersion().compareTo(tmpVersion) != 0)
			{
				tmpVersion = change.getTargetVersion();
				targetVersionsList.add(tmpVersion);
			}
		}

		return targetVersionsList;
	}

	//<editor-fold desc="Setters">

	//</editor-fold>

	//<editor-fold desc="Getters">

	public int getCurrentNumberOfChanges()
	{
		return this._changes.size();
	}

	//</editor-fold>

	//<editor-fold desc="Private Variables">

	private static ChangesLogger _instance = null;
	private ArrayList<Change> _changes = new ArrayList();
	
	private static Logger log = Logger.getLogger(ChangesLogger.class);

	//</editor-fold>
}
